home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Libraries / VideoToolbox 94.11.17 / VideoToolboxSources / GDTestClut.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-20  |  17.4 KB  |  558 lines  |  [TEXT/KAHL]

  1. /*
  2. GDTestClut.c
  3.  
  4. This is used by TimeVideo; I can’t think of any reason for including it in any
  5. other program.
  6.  
  7. OSErr GDTestClut(FILE *o[2],GDHandle device,short flags,VideoInfo *card);
  8.  
  9. GDTestClut.c tests whether the video clut can be written and read faithfully,
  10. and saves the results in a user-supplied VideoInfo structure. The test consists
  11. of writing random numbers to all the clut entries, reading them back, and
  12. comparing. I’ve been surprised to find more than a few video drivers that fail
  13. this test, for various reasons.
  14.  
  15. The bits of “flags” are tested independently. If flags&testClutQuicklyFlag then
  16. SetEntriesQuickly() will be tested instead of GDSetEntries/GDDirectSetEntries.
  17. If flags&testClutSeriallyFlag then the clut entries will be set individually,
  18. calling GDSetEntries once for each clut entry, to check the clut entry
  19. addressing. If flags&testClutLinearFlag then a simple sequence, will be loaded
  20. into the clut, instead of random numbers, to help figuring out systematic
  21. errors. The sequence is (0,0,0),(1,0,0), (0,2,0),(0,0,3),(4,4,4),(4,0,0), and so
  22. on.
  23.  
  24. Returned value is zero if ok, nonzero if error occurred.
  25.  
  26. GDTestClut recognizes the common driver errors and reports them in a sensible
  27. way, using the various fields of the card->depth[d].clut structure. Errors
  28. accumulate in the card->depth[d].clut structure, allowing you to make multiple
  29. calls to GDTestClut and only then summarize the results. It is important that
  30. you zero card->depth[d].clut.tests and card->depth[d].clutQuickly.tests before
  31. your first call to GDTestClut, to induce it to zero the rest of the clut
  32. structures.
  33.  
  34. Assumes that GDevice record is valid, i.e. the user has not called GDSetMode().
  35.  
  36. HISTORY:
  37. 3/9/93    dgp    code extracted from the demo TestCluts.c, to create a reusable
  38.         subroutine.
  39. 3/10/93    dgp    No longer assume that GDevice record reflects the actual state of the
  40.         driver.
  41. 4/5/93    dgp    Add support for grayB.
  42. 4/19/93    dgp    Fixed bug in VisibleHash that used garbage in place of
  43.             linear color table when gdType==directType. Use GDNewLinearColorTable.
  44. 5/11/93    dgp    GDTestClutHash now checks for valid device, in response to bug report
  45.             by Jonathan Brecher.
  46. 5/18/93    dgp    Allow small, 0.001, tolerance in establishing the identity transform.
  47. 5/25/93    dgp    Increased proportion of time spent loading clut in VisibleHash().
  48. 9/5/94 dgp removed assumption in printf's that int==short.
  49. */
  50. #include "VideoToolbox.h"
  51. #include <math.h>
  52. #include <Errors.h>
  53. #include "GDInfo.h"
  54.  
  55. // These functions are solely for use within this file.
  56. ColorSpec *MakeClutTable(GDHandle device,short flags);
  57. OSErr WriteClut(GDHandle device,ColorSpec putTable[],short flags);
  58. OSErr ShowGammaTable(FILE *o[2],GDHandle device);
  59. void RGBToGray(RGBColor *rgb,short dacSize);
  60. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask);
  61. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  62.     ,short *hashPtr);
  63. void GDRestoreBlackAndWhite(GDHandle device);
  64. OSErr EstimateClutTransform(short flags,VideoInfo *card);
  65. OSErr ComputeRMSClutError(FILE *file
  66.     ,ColorSpec *putTable,ColorSpec *getTable,short flags,VideoInfo *card);
  67. OSErr WriteAndReadClut(GDHandle device,short flags,ColorSpec **put,ColorSpec **get);
  68. #define SixteenBitGray 1
  69.  
  70. OSErr GDTestClut(FILE *o[2],short flags,VideoInfo *card)
  71. {
  72.     short i,j,clutSize,quickly,isGray;
  73.     int error;
  74.     ColorSpec *putTable=NULL,*getTable=NULL;
  75.     VideoCardClutTest *clut;
  76.     
  77.     if(card->device==NULL)return 0;
  78.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  79.     for(quickly=0;quickly<2;quickly++){
  80.         clut=&card->depth[card->d].clut[quickly][isGray];
  81.         if(!clut->read.tested){
  82.             for(j=0;j<3;j++)for(i=0;i<3;i++)clut->read.rgbGain[j][i]=NAN;
  83.             for(j=0;j<3;j++)clut->read.rgbError[j]=NAN;
  84.             for(j=0;j<3;j++)clut->read.rgbErrorAtOnce[j]=NAN;
  85.             clut->read.errors=0;
  86.             clut->read.errorsAtOnce=0;
  87.         }
  88.         if(!clut->visual.tested){
  89.             clut->visual.errors=0;
  90.             clut->visual.errorsAtOnce=0;
  91.         }
  92.         if(!clut->hash.tested){
  93.             clut->hash.errors=0;
  94.         }
  95.     }
  96.     quickly=((flags&testClutQuicklyFlag)!=0);
  97.     clut=&card->depth[card->d].clut[quickly][isGray];
  98.     if(!clut->read.doTest)return 0;
  99.     
  100.     GDInfo(card);
  101.     if(card->device==NULL)return 0;
  102.     clutSize=card->depth[card->d].clutSize;
  103.     error=GDSaveGamma(card->device);
  104.     error=GDUncorrectedGamma(card->device);
  105.     if(error)return error;
  106.     
  107.     error=EstimateClutTransform(flags&~testClutSeriallyFlag,card);
  108.     if(error)goto done;
  109.  
  110.     error=WriteAndReadClut(card->device,flags,&putTable,&getTable);
  111.     if(error)goto done;
  112.  
  113.     // COMPARE
  114.     ComputeRMSClutError(o[1],putTable,getTable,flags,card);
  115.     card->clutTested=1;
  116.     
  117.     done:
  118.     DisposePtr((Ptr)putTable);
  119.     DisposePtr((Ptr)getTable);
  120.     GDRestoreGamma(card->device);
  121.     GDRestoreDeviceClut(card->device);
  122.  
  123. //    clut->visual.doTest |= error && clut->hash.doTest;
  124. //    if(clut->visual.doTest)error=GDTestClutVisually(flags,card);
  125.     return error;
  126. }
  127.  
  128. OSErr EstimateClutTransform(short flags,VideoInfo *card)
  129. {
  130.     short i,j,tableSize,quickly,isGray;
  131.     int error=0;
  132.     ColorSpec *putTable,*getTable;
  133.     VideoCardClutTest *clut;
  134.     long putSum[3],getSum[3][3];
  135.     unsigned short *put3,*get3;
  136.         
  137.     error=WriteAndReadClut(card->device,flags|testClutGains,&putTable,&getTable);
  138.     if(error)return error;
  139.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  140.     
  141.     // Estimate transformation
  142.     for(i=0;i<3;i++){
  143.         putSum[i]=0;
  144.         for(j=0;j<3;j++)getSum[i][j]=0;
  145.     }
  146.     for(i=0;i<tableSize;i++){
  147.         put3=(unsigned short *)&putTable[i].rgb;
  148.         get3=(unsigned short *)&getTable[i].rgb;
  149.         putSum[i%3]+=put3[i%3];
  150.         for(j=0;j<3;j++)getSum[j][i%3]+=get3[j];
  151.     }
  152.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  153.     quickly=((flags&testClutQuicklyFlag)!=0);
  154.     clut=&card->depth[card->d].clut[quickly][isGray];
  155.     for(i=0;i<3;i++)for(j=0;j<3;j++){
  156.         clut->read.rgbGain[j][i]=(double)getSum[j][i]/putSum[i];
  157.         if(clut->read.rgbGain[j][i]!=floor(clut->read.rgbGain[j][i]))
  158.             clut->read.rgbGain[j][i]+=0.5*tableSize/putSum[i];
  159.     }
  160.     DisposePtr((Ptr)putTable);
  161.     DisposePtr((Ptr)getTable);
  162.     return error;
  163. }
  164.  
  165. OSErr WriteAndReadClut(GDHandle device,short flags,ColorSpec **put,ColorSpec **get)
  166. {
  167.     short i,clutSize,tableSize,error;
  168.     ColorSpec spec,*putTable,*getTable;
  169.     
  170.     clutSize=GDClutSize(device);
  171.  
  172.     // MAKE TABLE FOR CLUT
  173.     *put=putTable=MakeClutTable(device,flags);
  174.     if(putTable==NULL)return MemError();
  175.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  176.     
  177.     // MAKE BLANK TABLE
  178.     *get=getTable=(ColorSpec *)NewPtr(GetPtrSize((Ptr)putTable));
  179.     if(getTable==NULL)return MemError();
  180.     spec.value=spec.rgb.red=spec.rgb.green=spec.rgb.blue=0;
  181.     for(i=0;i<tableSize;i++)getTable[i]=spec;
  182.  
  183.     // WRITE CLUT & READ BACK, ONE CLUT-FULL AT A TIME
  184.     for(i=0;i<tableSize;i+=clutSize){
  185.         error=WriteClut(device,&putTable[i],flags);
  186.         if(error)return error;
  187.         error=GDGetEntries(device,0,clutSize-1,&getTable[i]);
  188.         if(error)return error;
  189.     }
  190.     return 0;
  191. }
  192.  
  193. OSErr ComputeRMSClutError(FILE *file
  194.     ,ColorSpec *putTable,ColorSpec *getTable,short flags,VideoInfo *card)
  195. {
  196.     short i,j,clutSize,tableSize,quickly,isGray;
  197.     unsigned short *put3,*get3,n[3],bad;
  198.     double e,squaredError[3],model[3];
  199.     VideoCardClutTest *clut;
  200.     int error=0;
  201.         
  202.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  203.     quickly=((flags&testClutQuicklyFlag)!=0);
  204.     clut=&card->depth[card->d].clut[quickly][isGray];
  205.     clutSize=GDClutSize(card->device);
  206.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  207.  
  208.     // Estimate error of linear model
  209.     for(i=0;i<3;i++){
  210.         squaredError[i]=0;
  211.         n[i]=0;
  212.     }
  213.     bad=0;
  214.     // Allow error of a few least significant steps of dac
  215.     clut->read.tolerance=2.9*(1<<(16-GDDacSize(card->device)));
  216.     for(i=0;i<tableSize;i++){
  217.         put3=(unsigned short *)&putTable[i].rgb;
  218.         get3=(unsigned short *)&getTable[i].rgb;
  219.         for(j=0;j<3;j++){
  220.             model[j]=put3[0]*clut->read.rgbGain[j][0]
  221.                 +put3[1]*clut->read.rgbGain[j][1]
  222.                 +put3[2]*clut->read.rgbGain[j][2];
  223.             e=get3[j]-model[j];
  224.             bad|=fabs(e)>clut->read.tolerance;
  225.             squaredError[j]+=e*e;
  226.             n[j]++;
  227.         }
  228.     }
  229.     for(i=0;i<3;i++){
  230.         clut->read.rgbError[i]=sqrt(squaredError[i]/n[i]);
  231.         if(!(flags&testClutSeriallyFlag))clut->read.rgbErrorAtOnce[i]=sqrt(squaredError[i]/n[i]);
  232.     }
  233.  
  234.     clut->read.identity=1;
  235.     for(i=0;i<3;i++)for(j=0;j<3;j++)clut->read.identity&=(fabs(clut->read.rgbGain[i][j]-(i==j))<0.001);
  236.     
  237.     if(bad){
  238.         clut->read.errors=1;
  239.         if(!(flags&testClutSeriallyFlag))clut->read.errorsAtOnce=1;
  240.     }
  241.     if(bad){
  242.         // Print one-line error message to file.
  243.         fprintf(file,"\n");
  244.         if(flags&testClutQuicklyFlag)fprintf(file,"SetEntriesQuickly != GDGetEntries. ");
  245.         else switch((*card->device)->gdType){
  246.         case fixedType:
  247.             break;
  248.         case clutType:
  249.             fprintf(file,"GDSetEntries != GDGetEntries. ");
  250.             break;
  251.         case directType:
  252.             fprintf(file,"GDDirectSetEntries != GDGetEntries. ");
  253.             break;
  254.         }
  255.         fprintf(file,"%d-bit ",(int)card->depth[card->d].pixelSize);
  256.         if(isGray)fprintf(file,"gray pixels.");
  257.         else fprintf(file,"color pixels.");
  258.         if(flags&testClutSeriallyFlag)fprintf(file," Loaded one clut entry at a time.\n");
  259.         else fprintf(file," Loaded whole clut at once.\n");
  260.  
  261.         // Print each clut error.
  262.         for(i=0;i<tableSize;i++){
  263.             put3=(unsigned short *)&putTable[i].rgb;
  264.             get3=(unsigned short *)&getTable[i].rgb;
  265.             bad=0;
  266.             for(j=0;j<3;j++){
  267.                 model[j]=put3[0]*clut->read.rgbGain[j][0]
  268.                     +put3[1]*clut->read.rgbGain[j][1]
  269.                     +put3[2]*clut->read.rgbGain[j][2];
  270.                 e=get3[j]-model[j];
  271.                 bad|=fabs(e)>clut->read.tolerance;
  272.             }
  273.             if(0 && bad)fprintf(file,"Clut[%3d] wrote(%04u,%04u,%04u) "
  274.                 "expected(%04.0f,%04.0f,%04.0f) but read(%04u,%04u,%04u)\n"
  275.                 ,i%clutSize,put3[0],put3[1],put3[2]
  276.                 ,model[0],model[1],model[2]
  277.                 ,get3[0],get3[1],get3[2]);
  278.             if(bad)fprintf(file,"Clut[%3d] wrote(%04x,%04x,%04x) "
  279.                 "expected(%04x,%04x,%04x) but read(%04x,%04x,%04x)\n"
  280.                 ,i%clutSize,put3[0],put3[1],put3[2]
  281.                 ,(unsigned short)(0.5+model[0]),(unsigned short)(0.5+model[1]),(unsigned short)(0.5+model[2])
  282.                 ,get3[0],get3[1],get3[2]);
  283.         }
  284.         fprintf(file,"\n");
  285.     }
  286.     clut->read.tested=1;
  287.     return error;
  288. }
  289.  
  290. OSErr GDTestClutVisually(short flags,VideoInfo *card)
  291. {
  292.     int error;
  293.     short clutSize,quickly,isGray;
  294.     ColorSpec *putTable,*normalTable;
  295.     Boolean weirdError,normalError;
  296.     VideoCardClutTest *clut;
  297.     
  298.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  299.     quickly=((flags&testClutQuicklyFlag)!=0);
  300.     clut=&card->depth[card->d].clut[quickly][isGray];
  301.     if(clut->visual.tested==0){
  302.         clut->visual.errors=0;
  303.         clut->visual.errorsAtOnce=0;
  304.     }
  305.     if(card->device==NULL)return 0;
  306.     clutSize=GDClutSize(card->device);
  307.     normalTable=((**(**(**card->device).gdPMap).pmTable)).ctTable;
  308.     if(card->device==GetMainDevice())
  309.         putTable=MakeClutTable(card->device,testClutNegativeFlag);
  310.     else putTable=MakeClutTable(card->device,flags);
  311.     if(putTable==NULL)return MemError();
  312.     GDSaveGamma(card->device);
  313.     GDUncorrectedGamma(card->device);
  314.     error=WriteClut(card->device,putTable,flags);
  315.     if(error){
  316.         GDRestoreGamma(card->device);
  317.         GDRestoreDeviceClut(card->device);
  318.         return error;
  319.     }
  320.     printf(BLANKLINE);
  321.     printf("Screen should be weirdly colored; watch for subtle change as you hit return:" "\r");
  322.     while(getcharUnbuffered()==-1) ;
  323.     error=WriteClut(card->device,putTable,0);
  324.     if(error){
  325.         GDRestoreGamma(card->device);
  326.         GDRestoreDeviceClut(card->device);
  327.         return error;
  328.     }
  329.     printf(BLANKLINE);
  330.     printf("Did you see any change at all?");
  331.     weirdError=YesOrNo(0);
  332.     printf("\r");
  333.     error=WriteClut(card->device,normalTable,flags);
  334.     printf(BLANKLINE);
  335.     printf("Screen should be normal now; watch for subtle change as you hit return:" "\r");
  336.     while(getcharUnbuffered()==-1) ;
  337.     error=WriteClut(card->device,normalTable,0);
  338.     printf(BLANKLINE);
  339.     printf("Did you see any change at all?");
  340.     normalError=YesOrNo(0);
  341.     printf("\r");
  342.     GDRestoreGamma(card->device);
  343.     GDRestoreDeviceClut(card->device);
  344.     DisposePtr((Ptr)putTable);
  345.     clut->visual.tested++;
  346.     clut->visual.errors+=(weirdError || normalError);
  347.     if(!(flags&testClutSeriallyFlag))clut->visual.errorsAtOnce+=(weirdError || normalError);
  348.     return 0;
  349. }
  350.  
  351. ColorSpec *MakeClutTable(GDHandle device,short flags)
  352. {
  353.     short i,j,clutSize;
  354.     RGBColor put;
  355.     ColorSpec *table;
  356.     
  357.     clutSize=GDClutSize(device);
  358.     if((flags&testClutGains) && clutSize<16)clutSize=16;
  359.     table=(ColorSpec *)NewPtr(sizeof(*table)*clutSize);
  360.     if(table==NULL)return table;
  361.     for(i=0;i<clutSize;i++) {
  362.         if(flags&testClutNegativeFlag){
  363.             put=((**(**(**device).gdPMap).pmTable)).ctTable[clutSize-1-i].rgb;
  364.         }else if(flags&testClutGains){
  365.             // Estimate rgb gains of any transformation
  366.             put.red=put.green=put.blue=0;
  367.             j=(0xffffL*i+(clutSize-1)/2)/(clutSize-1);
  368.             switch(i%3){
  369.             case 0:
  370.                 put.red=j;
  371.                 break;
  372.             case 1:
  373.                 put.green=j;
  374.                 break;
  375.             case 2:
  376.                 put.blue=j;
  377.                 break;
  378.             }
  379.         }else if(flags&testClutLinearFlag){
  380.             // Linear test pattern
  381.             put.red=put.green=put.blue=0;
  382.             j=0xffffffff*(i+(clutSize-1)/2)/(clutSize-1);
  383.             switch(i%4){
  384.             case 0:
  385.                 put.red=put.green=put.blue=j;
  386.                 break;
  387.             case 1:
  388.                 put.red=j;
  389.                 break;
  390.             case 2:
  391.                 put.green=j;
  392.                 break;
  393.             case 3:
  394.                 put.blue=j;
  395.                 break;
  396.             }
  397.         }else{
  398.             // Random test pattern
  399.             put.red=randU();
  400.             put.green=randU();
  401.             put.blue=randU();
  402.         }
  403.         if(!SixteenBitGray){
  404.             put.red&=0xff00;
  405.             put.green&=0xff00;
  406.             put.blue&=0xff00;
  407.         }
  408.         table[i].rgb=put;
  409.     }
  410.     return table;
  411. }
  412.  
  413. OSErr WriteClut(GDHandle device,ColorSpec putTable[],short flags)
  414. {
  415.     short i,clutSize=GDClutSize(device);
  416.     char priority=7;
  417.     int error;
  418.     SetEntriesFunction function;
  419.         
  420.     if(flags&testClutQuicklyFlag)function=SetEntriesQuickly;
  421.     else function=GDSetEntriesByType;
  422.     if(flags&testClutSeriallyFlag){
  423.         // Load one clut entry at a time
  424.         for(i=0;i<clutSize;i++){
  425.             SwapPriority(&priority);    // Force driver to load clut now.
  426.             error=(function)(device,i,0,&putTable[i]);
  427.             SwapPriority(&priority);
  428.             if(error)return error;
  429.         }
  430.     }else{
  431.         // Load whole clut at once
  432.         SwapPriority(&priority);    // Force driver to load clut now.
  433.         error=(function)(device,0,clutSize-1,putTable);
  434.         SwapPriority(&priority);
  435.         if(error)return error;
  436.     }
  437.     return 0;
  438. }
  439.  
  440. OSErr GDTestClutHash(short flags,VideoInfo *card){
  441.     SetEntriesFunction function;
  442.     int error;
  443.     short isGray,quickly;
  444.     VideoCardClutTest *clut;
  445.     
  446.     if(card->device==NULL)return 0;
  447.     quickly=((flags&testClutQuicklyFlag)!=0);
  448.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  449.     clut=&card->depth[card->d].clut[quickly][isGray];
  450.     if(clut->hash.tested==0)clut->hash.errors=0;
  451.     if(!clut->hash.doTest)return 0;
  452.     if(quickly)function=SetEntriesQuickly;
  453.     else function=GDSetEntriesByType;
  454.     error=VisibleHash(card->device,function,0,&clut->hash.errors);
  455.     clut->hash.tested=1;
  456.     return error;
  457. }
  458.  
  459. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  460.     ,short *hashPtr)
  461. {
  462.     int error;
  463.     short clutSize;
  464.     short hash;
  465.     long tick;
  466.     ColorSpec *putTable,*linearTable=NULL;
  467.  
  468.     if(device==NULL || (**device).gdType==fixedType)return 0;
  469.     clutSize=GDClutSize(device);
  470.     if(clutEntries<0 || clutEntries>clutSize)return 1;
  471.     if(clutEntries==0)clutEntries=clutSize;
  472.     if((**device).gdType==directType){
  473.         if(function==GDSetEntries)function=GDDirectSetEntries;
  474.         putTable=linearTable=GDNewLinearColorTable(device);
  475.         if(linearTable==NULL)return MemError();
  476.     }else putTable=((**(**(**device).gdPMap).pmTable)).ctTable;
  477.     error=(function)(device,0,clutEntries-1,putTable);
  478.     if(!error){
  479.         printf(BLANKLINE);
  480.         if(device==GetMainDevice())
  481.             printf("Do you see any dynamic black specks on this screen? (No):");
  482.         else printf("Do you see any dynamic black specks on the test screen? (No):");
  483.         fflush(stdout);
  484.         do{
  485.             tick=TickCount();
  486.             do{
  487.                 (function)(device,0,clutEntries-1,putTable);
  488.             }while(TickCount()-tick<30);
  489.         }while(!kbhit());
  490.         hash=YesOrNo(0);
  491.         printf("\r");
  492.         if(linearTable!=NULL)DisposePtr((Ptr)linearTable);
  493.         if(hashPtr!=NULL)*hashPtr=hash;
  494.     }
  495.     return error;
  496. }
  497.  
  498. void GDRestoreBlackAndWhite(GDHandle device)
  499. // Restore the first & last clut entries to white and black.
  500. {
  501.     short clutSize;
  502.     int error;
  503.     ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  504.  
  505.     clutSize=GDClutSize(device);
  506.     switch((**device).gdType){
  507.     case clutType:
  508.         error=GDSetEntries(device,0,0,&white);
  509.         error=GDSetEntries(device,clutSize-1,0,&black-(clutSize-1));
  510.         break;
  511.     case directType:
  512.         error=GDDirectSetEntries(device,0,0,&black);
  513.         error=GDDirectSetEntries(device,clutSize-1,0,&white-(clutSize-1));
  514.         break;
  515.     default:
  516.         break;
  517.     }
  518. }
  519. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask)
  520. {
  521.     return (a->red&mask)!=(b->red&mask)
  522.         ||(a->green&mask)!=(b->green&mask)
  523.         ||(a->blue&mask)!=(b->blue&mask);
  524. }
  525.  
  526. /*
  527. When you set a video screen to monochrome or "gray" (as opposed to "color"),
  528. e.g. using the Control Panel:Monitors, the request is passed on to the video
  529. driver. The video driver transforms each of your rgb triplets to a
  530. luminance-equivalent gray, using a formula that must be very similar, if not
  531. equivalent, to the code below. The rounding is bad, e.g. any gray rgb triplet
  532. (i,i,i), other than (0,0,0), is transformed to (i-1,i-1,i-1), which is darker,
  533. failing to preserve luminance. However, my goal was to replicate Apple's crumby
  534. transformation, not to improve it. I presume that the reason that I have to trim
  535. my numbers down a tad (-0.00001) is that I'm doing this with 80-bit precision
  536. whereas the driver uses the 64-bit precision of the SANE routines. Presumably I
  537. could obtain the same result by compiling this subroutine separately, to use
  538. 64-bit floating point, since all the arguments are ints.
  539. */
  540. void RGBToGray(RGBColor *rgb,short dacSize)
  541. // Empirical formula to replicate Apple's luminance mapping.
  542. {
  543.     short i,shift;
  544.     unsigned long n;
  545.     
  546.     if(!SixteenBitGray){
  547.         shift=16-dacSize;
  548.         i=(rgb->red>>shift)*(0.30-0.00001)
  549.             +(rgb->green>>shift)*(0.59-0.00001)+(rgb->blue>>shift)*(0.11);
  550.         n=0xffffffffUL/((1<<dacSize)-1);
  551.         rgb->red=rgb->green=rgb->blue=(i*n)>>16;
  552.     }else{
  553.         rgb->red=rgb->green=rgb->blue=rgb->red*(0.30-0.00001)
  554.             +rgb->green*(0.59)+rgb->blue*(0.11-0.00001);
  555.     }
  556. }
  557.  
  558.